/*
 * Copyright (c) 2025 Li Auto Inc. and its affiliates
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef MVBS_RPC_H
#define MVBS_RPC_H

#include <mvbs/base_type.h>
#include <mvbs/errno.h>
#include <mcdr/mcdr.h>

#define RPC_HEAD_MAGIC		0xfb709394U
#define RPC_INVALID_SN		((uint32_t)-1)
#define RPC_HEADER_SIZE		(uint32_t)sizeof(struct rpc_header)

/* rpc message type*/
#define RPC_TYPE_FAIL		((uint32_t)-1)
#define RPC_TYPE_NORM_REQ	0
#define RPC_TYPE_NORM_RES	1
#define RPC_TYPE_STREAM_REQ	2
#define RPC_TYPE_STREAM_RES	3
#define RPC_TYPE_STREAM_STOP	4
#define RPC_TYPE_MAX		5 /* reserved, current no use */

/* rpc error number */
#define RPC_ERRNO_TIMEOUT       (-1002)
#define RPC_ERRNO_CONNCLOSE     (-1003)
#define RPC_ERRNO_INVALID       (-1004)
#define RPC_ERRNO_IFNOTFOUND    (-1005)
#define RPC_ERRNO_OPNOTFOUND    (-1006)
#define RPC_ERRNO_SERVERERROR   (-1007)

struct rpc_header {
	uint32_t	magic_num;
	uint32_t	msg_type;
	uint32_t	sn;
	uint32_t	body_len;
	uint32_t	reserve[4];
};

/* -------------------------------------------------------------------
 *			PUBLIC INTERFACE (COMMON)
 * ------------------------------------------------------------------- */

/**
 * Serialize the message header with given parameters (except magic number),
 * and put the serialized header into the buffer of mvbs_cdr struct.
 *
 * @param mcdr_stream the pointer to mvbs_cdr struct
 * @param msg_type message type
 * @param seq_no sequence number
 * @param body_len length of message body
 * @return 0 if success，otherwise an error number
 */
int32_t rpc_header_serialize(struct mvbs_cdr *mcdr_stream,
			 uint32_t msg_type, uint32_t seq_no, uint32_t body_len);

/**
 * Deserialize the message header in the buffer of mvbs_cdr struct,
 * and assign the deserialized values to the given rpc_header struct.
 *
 * @param mcdr_stream the pointer to mvbs_cdr struct
 * @param header the pointer to rpc_header struct with deserialized values
 * @return 0 if success，otherwise an error number
 */
int32_t rpc_header_deserialize(struct mvbs_cdr *mcdr_stream, struct rpc_header *header);

/* -------------------------------------------------------------------
 *			PUBLIC INTERFACE (CLIENT)
 * ------------------------------------------------------------------- */

/* abstraction of the rpc client */
struct rpc_client;


/* pointer to the callback function, which is generated by IDL. */
typedef int32_t (*rpc_client_cb_t)(struct mvbs_cdr *stream, void *arg,
						int32_t error_flag);

/**
 * Create a new rpc client object with a socket and configure server ip
 * address and port number with the given parameters.
 *
 * @param server_addr ip address pf server
 * @param port port number
 * @return a pointer to the newly created rpc_client struct if success，otherwise NULL
 */
struct rpc_client *rpc_client_new(const char *server_addr, const uint16_t port);

/**
 * Connect the server with specific ip address and port number that already
 * configured in rpc_client struct.
 *
 * @param cli the pointer to rpc_client struct
 * @return 0 if success，otherwise an error number
 */

int32_t rpc_client_connect(struct rpc_client *cli);

/**
 * Destroy the rpc client object and close the socket.
 *
 * @param cli the pointer to rpc_client struct that need to be destroyed
 */
void rpc_client_destroy(struct rpc_client *cli);

/**
 * Check the client object and its socket is valid or not.
 * Note: if a client is already destoryed, it will be treated as invalid.
 *
 * @param cli the pointer to rpc_client struct that need to be checked
 * @return true if cli is valid，otherwise false
 */
bool rpc_client_is_valid(struct rpc_client *cli);

/**
 * Register the callback function in the handler struct, get the sequence
 * number and config other parameters.
 *
 * Note:
 * For timeout, 0 means immediately handle timeout, -1 means timeout
 * never happen.
 * For preset_sn, if it is the first time to call this interface, the
 * value should be RPC_INVALID_SN. If rpc type is stream ,the preset_sn
 * can be used again.
 *
 * @param cli the pointer to rpc_client struct
 * @param timeout value of timeout
 * @param cb callback function to register in the handler
 * @param arg callback function defined by user
 * @param is_stream flag of rpc type, normal or stream
 * @param preset_sn sequence number that is already set
 * @return the sequence number allocated if success, otherwise RPC_INVALID_SN
 */
uint32_t rpc_client_alloc_sn(struct rpc_client *cli, uint32_t timeout,
			     rpc_client_cb_t cb, void *arg, int32_t is_stream,
			     uint32_t preset_sn);

/**
 * Free the sequence number and reset the handler struct
 *
 * @param cli the pointer to rpc_client struct
 * @param sn sequence number to be freed
 */
void rpc_client_free_sn(struct rpc_client *cli, uint32_t sn);

/**
 * Send request message to the server.
 *
 * @param cli the pointer to the rpc_client struct that need to send data
 * @param buf buffer of data to be sent
 * @param len length of data to be sent
 * @return the number of bytes sent if success，otherwise an error number
 */
int32_t rpc_client_send(struct rpc_client *cli, const char *buf, uint32_t len);

/**
 * Receive the response from server, deserialize and check the message, call
 * the registered callback function registered in the corresponding handler
 * and handle timeout.
 *
 * @param cli the pointer to the rpc_client struct that need to receive data
 * @param timer_cnt current time
 * @param buf buffer to receive data
 * @param buf_size size of buffer
 */
void rpc_client_recv_loop(struct rpc_client *cli, uint32_t timer_cnt,
					    char *buf, uint32_t buf_size);


/* -------------------------------------------------------------------
 *			PUBLIC INTERFACE (SERVER)
 * ------------------------------------------------------------------- */

/* abstraction of the rpc server */
struct rpc_server;

/* abstraction of the connection to each client of a rpc server */
struct rpc_connection;

typedef int32_t (*rpc_server_cb_t)(struct rpc_connection *conn,
			struct mvbs_cdr *stream, struct rpc_header *header);

/**
 * Create and initialize a new rpc server object with a socket, configure the given
 * parameters, bind with an address and begin to listen.
 *
 * @param port port number
 * @param max_connects max number of clients that can connect this server
 * @param cb service entry of all remote procedure calls
 * @return a pointer to the newly created rpc_server if success，otherwise NULL
 */
struct rpc_server *rpc_server_new(const uint16_t port, uint16_t max_connects,
						 rpc_server_cb_t cb);

/**
 * Destroy the rpc server object and close the socket.
 *
 * @param srv the pointer to rpc_server struct that need to be destroyed
 */
void rpc_server_destroy(struct rpc_server *srv);

/**
 * Send response message to the client identified by rpc_connection.
 *
 * @param conn the pointer to the rpc_connection struct that need to send data
 * @param buf buffer of data to be sent
 * @param len length of data to be sent
 * @return the number of bytes sent if success，otherwise an error number
 */
int32_t rpc_connection_send(struct rpc_connection *conn, const char *buf, uint32_t len);

/**
 * Accept and create rpc_connection struct for each connection, receive the requests
 * from all the corresponding clients, deserialize and check of the message, call
 * the service entry function.
 *
 * @param srv the pointer to the rpc_server struct that need to receive data
 * @param buf buffer to receive data
 * @param buf_size size of buffer
 */
void rpc_server_recv_loop(struct rpc_server *srv, char *buf, uint32_t buf_size);

#endif
